/* Original Author:  Dominick O'Dierno
 * Date: 2007   odiernod@gmail.com     --Originally intended for use with DOMercury:  http://www.odierno.com/domercury
 * Description: Uses ManagedWinapi and other Win32 API functions to grab the selected items from a Windows Explorer Window
 * It currently uses the address bar to finish the file path of the items
 * 
 * --CHANGELOG--
 * 
 * 
 * --TODO--
 * Include a way to successfully grab selected items from the Desktop as well.
 * 
 * Discover a way to retrieve the path of the items grabbed from Windows Explorer without needing to read the address bar
 * */

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using ManagedWinapi;
using ManagedWinapi.Windows;
using DOMercury.Items;

namespace DOMercury.Managers
{
    class SelectedItemsGrabber
    {
        private static bool IsExplorerWindow(SystemWindow w)
        {
            if ((w.Process.ProcessName == "explorer") && (w.ClassName == "CabinetWClass"))
            {
                return true;
            }
            else
            {
                return false;
            }
        }       

        public static ItemsList GetSelectedEnvironmentItems()
        {
            List<PathItem> items = new List<PathItem>();
            ManagedWinapi.Windows.SystemWindow[] explorerwindows = new ManagedWinapi.Windows.SystemWindow[1] {ActionManager.ForegroundWindow};

            //ManagedWinapi.Windows.SystemWindow[] explorerwindows = new ManagedWinapi.Windows.SystemWindow[1] {ManagedWinapi.Windows.SystemWindow.ForegroundWindow};
            //ManagedWinapi.Windows.SystemWindow[] explorerwindows = ManagedWinapi.Windows.SystemWindow.FilterToplevelWindows(IsExplorerWindow);
            SystemWindow ListViewWindow;
            try
            {
                ListViewWindow = SystemListViewHelper.FindListView32(explorerwindows[0]);
                string path = SystemListViewHelper.DirectoryPath(explorerwindows[0]);
                SystemListViewHelper.SelectedItems(ListViewWindow, path, ref items);
                foreach (PathItem p in items)
                {
                    IconManager.AddIcon(p.Path);
                }
                return new ItemsList(items);
            }
            catch
            {
                return new ItemsList(new List<PathItem>());
            }
       }
    }

    public class SystemListViewHelper
    {
        /// <summary>
        /// Retrieves selected items from a SystemWindow(ListView, TreeView, Desktop)
        /// </summary>
        /// <param name="ListView32">The SystemWindow to retrieve selected items from</param>
        /// <returns>A dictionary holding the selected items</returns>
        public static void SelectedItems(SystemWindow ListView32, string path, ref List<PathItem> list)
        {
            if (path != "")
            {
                list.Clear();

                int index = -1;
                SystemListView lv = SystemListView.FromSystemWindow(ListView32);
                index = Win32.SendMessage(ListView32.HWnd, Win32.LVM_GETNEXTITEM, index, Win32.LVNI_SELECTED);
                while (index != -1)
                {
                    string filename = lv[index].Title;
                    list.Add(new PathItem(PathFinder.FindPath(path) + "\\" + filename, filename));
                    index = Win32.SendMessage(ListView32.HWnd, Win32.LVM_GETNEXTITEM, index, Win32.LVNI_SELECTED);
                }
            }
            
        }

        /// <summary>
        /// Get the path of the provided Windows Explorer Dirctory
        /// </summary>
        /// <param name="ExplorerWindow">The provided Windows Explorer Window</param>
        /// <returns>A path to the provided Windows Explorer Directory</returns>
        public static string DirectoryPath(SystemWindow ExplorerWindow)
        {
            string result = "";
            if (System.Environment.OSVersion.Version.Major == 5)
            {
                //XP
                result = XPDirectoryPath(ExplorerWindow);
            }
            else if (System.Environment.OSVersion.Version.Major == 6)
            {
                //Vista
                result = VistaDirectoryPath(ExplorerWindow);
            }
            else
            {
                //Screwed
            }
            //MessageBox.Show(result);
            return result;
        }

        private static string XPDirectoryPath(SystemWindow ExplorerWindow)
        {
            //Find WorkerW
            if (FindChildByClass(ExplorerWindow, "WorkerW", 0) != -1)
            {

                SystemWindow temp = ExplorerWindow.AllChildWindows[FindChildByClass(ExplorerWindow, "WorkerW", 0)];
                //Find ReBarWindow32
                temp = temp.AllChildWindows[FindChildByClass(temp, "ReBarWindow32", 0)];
                //Find ComboBoxEx32
                temp = temp.AllChildWindows[FindChildByClass(temp, "ComboBoxEx32", 0)];
                //Find ComboBox
                temp = temp.AllChildWindows[FindChildByClass(temp, "ComboBox", 0)];
                Regex rx = new Regex(@"[a-zA-Z]:(\\.*)*");
                MatchCollection m = rx.Matches(temp.Content.LongDescription.Replace("\n", "^"));
                string result = m[4].Value.Split('^')[0].Trim();
                return result;
            }
            return "";
        }

        private static string VistaDirectoryPath(SystemWindow ExplorerWindow)
        {

            //Find WorkerW
            SystemWindow temp = ExplorerWindow.AllChildWindows[FindChildByClass(ExplorerWindow, "WorkerW", 0)];
            //Find ReBarWIndow32
            temp = temp.AllChildWindows[FindChildByClass(temp, "ReBarWindow32", 0)];
            //Find Address Band Root
            temp = temp.AllChildWindows[FindChildByClass(temp, "Address Band Root", 0)];
            //Find msctls_progress32
            temp = temp.AllChildWindows[FindChildByClass(temp, "msctls_progress32", 0)];
            //Find msctls_progress32
            temp = temp.AllChildWindows[FindChildByClass(temp, "ToolbarWindow32", 0)];
            Regex rx = new Regex(@"[a-zA-Z]:(\\.*)*");
            MatchCollection m = rx.Matches(temp.Content.LongDescription.Replace("\n", "^"));
            foreach (Match mtch in m)
            {
                if (mtch.Value.Contains("push button"))
                {
                    return mtch.Value.Split('\"')[0].Trim();
                }
            }
            return "";
        }


        #region FindListView
        /// <summary>
        /// Finds the ListView32 from the top level explorer window
        /// </summary>
        /// <param name="ExplorerWindow">The provided top level explorer window</param>
        /// <returns>The ListView32 window</returns>
        public static SystemWindow FindListView32(SystemWindow ExplorerWindow)
        {
            SystemWindow result = ExplorerWindow;
            if (System.Environment.OSVersion.Version.Major == 5)
            {
                //XP
                result = XPFindListView32(ExplorerWindow);
            }
            else if (System.Environment.OSVersion.Version.Major == 6)
            {                   
                //Vista
                result = VistaFindListView32(ExplorerWindow);
            }
            else
            {
                //Screwed
                System.Windows.Forms.MessageBox.Show("Sorry, This functionality is currently only supported in WindowsXP and Vista");
            }

            //result = result.AllDescendantWindows[FindDescendantByClass(result, "SysListView32", 0)];
            return result;
        }

        private static SystemWindow VistaFindListView32(SystemWindow ExplorerWindow)
        {
            SystemWindow result = ExplorerWindow;
            try
            {
                return ExplorerWindow.FilterDescendantWindows(false, delegate(SystemWindow sw) { return sw.ClassName == "SysListView32"; })[0];
                ////Find ShellTabWindowClass
                //result = ExplorerWindow.AllChildWindows[FindChildByClass(ExplorerWindow, "ShellTabWindowClass", 0)];
                ////Find DirectUIHWND
                //result = result.AllChildWindows[FindChildByClass(result, "DUIViewWndClassName", 0)];

                //result = result.AllChildWindows[FindChildByClass(result, "DirectUIHWND", 0)];
                ////Find Last CtrlNotifySink
                //result = result.AllChildWindows[FindChildByClassDesc(result, "CtrlNotifySink", result.AllChildWindows.Length - 1)];
                ////Find SHELLDLL_DefView
                //result = result.AllChildWindows[FindChildByClass(result, "SHELLDLL_DefView", 0)];
                ////Find SysListView32
                //result = result.AllChildWindows[FindChildByClass(result, "SysListView32", 0)];
            }
            catch { return ExplorerWindow; }
            //return result;
        }

        private static SystemWindow XPFindListView32(SystemWindow ExplorerWindow)
        {
            //SystemWindow result = ExplorerWindow;
            try
            {
                SystemWindow[] DescendantWindows = ExplorerWindow.FilterDescendantWindows(false, delegate(SystemWindow sw) { return sw.ClassName == "SysListView32"; });
                if (DescendantWindows.Length != 0)
                {
                    return DescendantWindows[0];
                }
                else
                {
                    return ExplorerWindow;
                }

                ////Find SHELLDLL_DefView
                //result = result.AllChildWindows[FindChildByClass(result, "SHELLDLL_DefView", 0)];
                ////Find SysListView32
                //result = result.AllChildWindows[FindChildByClass(result, "SysListView32", 0)];
            }
            catch { return ExplorerWindow; }
            //return result;
        }
        #endregion

        /// <summary>
        /// Finds the next child window with the provided classname in ascending order
        /// </summary>
        /// <param name="Window">The Window of which the child windows will be searched</param>
        /// <param name="ClassName">The classname to find</param>
        /// <param name="StartIndex">The search start index</param>
        /// <returns>The index of the located window.  Returns -1 if not found</returns>
        public static int FindChildByClass(SystemWindow Window, string ClassName, int StartIndex)
        {
            try
            {
                for (int i = StartIndex; i < Window.AllChildWindows.Length; i++)
                {
                    if (Window.AllChildWindows[i].ClassName == ClassName)
                    {
                        return i;
                    }
                }
                return -1;
            }
            catch
            {
                return -1;
            }
        }
        /// <summary>
        /// Finds the next child window with the provided classname in descending order
        /// </summary>
        /// <param name="Window">The Window of which the child windows will be searched</param>
        /// <param name="ClassName">The classname to find</param>
        /// <param name="StartIndex">The search start index</param>
        /// <returns>The index of the located window.  Returns -1 if not found</returns>
        public static int FindChildByClassDesc(SystemWindow Window, string ClassName, int StartIndex)
        {
            try
            {
                for (int i = StartIndex; i > -1; i--)
                {
                    if (Window.AllChildWindows[i].ClassName == ClassName)
                    {
                        return i;
                    }
                }
                return -1;
            }
            catch
            {
                return -1;
            }
        }

        /// <summary>
        /// Finds the next Descendant window with the provided classname in ascending order
        /// </summary>
        /// <param name="Window">The Window of which the Descendant windows will be searched</param>
        /// <param name="ClassName">The classname to find</param>
        /// <param name="StartIndex">The search start index</param>
        /// <returns>The index of the located window.  Returns -1 if not found</returns>
        public static int FindDescendantByClass(SystemWindow Window, string ClassName, int StartIndex)
        {
            try
            {
                for (int i = StartIndex; i < Window.AllDescendantWindows.Length; i++)
                {
                    if (Window.AllDescendantWindows[i].ClassName == ClassName)
                    {
                        return i;
                    }
                }
                return -1;
            }
            catch
            {
                return -1;
            }
        }
        /// <summary>
        /// Finds the next Descendant window with the provided classname in descending order
        /// </summary>
        /// <param name="Window">The Window of which the Descendant windows will be searched</param>
        /// <param name="ClassName">The classname to find</param>
        /// <param name="StartIndex">The search start index</param>
        /// <returns>The index of the located window.  Returns -1 if not found</returns>
        public static int FindDescendantByClassDesc(SystemWindow Window, string ClassName, int StartIndex)
        {
            try
            {
                for (int i = StartIndex; i > -1; i--)
                {
                    if (Window.AllDescendantWindows[i].ClassName == ClassName)
                    {
                        return i;
                    }
                }
                return -1;
            }
            catch
            {
                return -1;
            }
        }
        #region FindTreeView
        /// <summary>
        /// Finds the TreeView32 of the top level Explorer Window
        /// </summary>
        /// <param name="ExplorerWindow"></param>
        /// <returns></returns>
        public static SystemWindow FindTreeView32(SystemWindow ExplorerWindow)
        {
            SystemWindow result = ExplorerWindow;
            //if (System.Environment.OSVersion.Version.Major == 5)
            //{
            //    //XP
            //}
            //else if (System.Environment.OSVersion.Version.Major == 6)
            //{
            //    //Vista
            //    result = VistaFindTreeView32(ExplorerWindow);
            //}
            //else
            //{
            //    //Screwed
            //    MessageBox.Show("Sorry, This functionality is currently only supported in WindowsXP and Vista");
            //}
            result = result.AllDescendantWindows[FindDescendantByClass(result, "SysTreeView32", 0)];
            return result;
        }

        private static SystemWindow VistaFindTreeView32(SystemWindow ExplorerWindow)
        {
            SystemWindow result = ExplorerWindow;
            //Finde ShellTabWindowClass
            result = result.AllChildWindows[FindChildByClass(result, "ShellTabWindowClass", 0)];
            //Find DUIViewWndClassName
            result = result.AllChildWindows[FindChildByClass(result, "DUIViewWndClassName", 0)];
            //Find CtrlNotifySink with NamespaceTreeControl
            bool found = false;
            SystemWindow tbase = result;
            for (int i = 0; i < result.AllChildWindows.Length; i++)
            {
                tbase = result.AllChildWindows[i];
                if (FindChildByClass(tbase, "NamespaceTreeControl", 0) != -1)
                {
                    result = tbase.AllChildWindows[FindChildByClass(tbase, "NamespaceTreeControl", 0)];
                    found = true;
                }
            }
            if (!found)
            {
                result = ExplorerWindow;
                return result;
            }
            //Find SysTreeView32
            result = result.AllChildWindows[FindChildByClass(result, "SysTreeView32", 0)];
            return result;
        }
        #endregion
    }

}
